// @author U099151W Sun Chenchen
// all functions in this file  are implemented by the author unless specifically indicated
var CAMERA_THRESHOLD = 100;
var Camera = function() {
    // to enforce singleton
    if ( arguments.callee._singletonInstance )
        return arguments.callee._singletonInstance;
    arguments.callee._singletonInstance = this;

    this.posX = 400;
    this.posY = 125;
    this.viewWidth = 800;
    this.viewHeight = 200;
}
Camera.prototype.update = function(scene) {
    if (this.posX + this.viewWidth/2 - CAMERA_THRESHOLD < scene.Player.x) {
        var diff = scene.Player.x - (this.posX + this.viewWidth/2 - CAMERA_THRESHOLD);
        this.posX += diff;
        if (this.posX - this.viewWidth/2 < 0) {
            this.posX = this.viewWidth / 2;
        }
    }
    if (this.posX - this.viewWidth/2 + CAMERA_THRESHOLD > scene.Player.x) {
        var diff = (this.posX - this.viewWidth/2 + CAMERA_THRESHOLD) - scene.Player.x;
        this.posX -= diff;
        if (this.posX - this.viewWidth/2 < 0) {
            this.posX = this.viewWidth / 2;
        }
    }
}
var camera = new Camera();

// @author U095159L VARUN GANESH
var Obstacle = function(name, index, render_x, render_y, size, trans_x, trans_y){
    this.name = name;
    this.index = index;
    this.x_disp = render_x;
    this.y_disp = render_y;
    this.resize = size;
    this.trans_x = trans_x;
    this.trans_y = trans_y;
}

var GraphicEngine = function() {

    // to enforce singleton
    if ( arguments.callee._singletonInstance )
        return arguments.callee._singletonInstance;
    arguments.callee._singletonInstance = this;

    var _FPS;

    var _UI_canvas = null;
    var _background_canvas = null;
    var _game_canvas = null;
    var _progress_bar_canvas = null;
    var _debug_canvas = null;
    var _tutorial_canvas = null;
    var _tutorial_imageIndex = 0;

    var _scene;
    var _imageLib;

    var _imagesLoadedNum;
    var _imagesToLoadNum;

    var _obstacleGrid = [];
    var _obstacleMargin = 2;

    function imageOnLoad() {
        _imagesLoadedNum++;
        _imageLib.setItem(this.id, this);

        var loading = document.createElement('p');
        loading.innerHTML = "loading sprites";
        document.getElementById("debug").appendChild(loading);

        if (_imagesLoadedNum < _imagesToLoadNum)
            return;
        var loaded = document.createElement('p');
        loaded.innerHTML = "all sprites loaded";
        document.getElementById("debug").innerHTML =loaded.innerHTML;

        // init TILE_MAP
        var backgroundImage = _imageLib.getItem(_scene.backgroundList[_scene.Stages[_scene.currentStage].index].name);
        var row = Math.floor(_game_canvas.height / TILE_RESOLUTION);
        var col = Math.floor(backgroundImage.width / TILE_RESOLUTION); // TEMP hack
        TILE_MAP.ROWS = row;
        TILE_MAP.COLS = col;
        TILE_MAP.GRIDS = [];
        for (var i=0; i<row; i++) {
            var newRow = [];
            _obstacleGrid[i] = [];
            for (var j=0; j< col; j++) {
                newRow.push(0);
                _obstacleGrid[i][j] = 0;
            }
            TILE_MAP.GRIDS.push(newRow);
        }

        setObstacles();
        graphicEngine.renderTutorial();
    }

    this.renderTutorial = function() {
        if (_tutorial_imageIndex < sceneDef.getTutorialImageList().length) {
            var tutorialImage = _imageLib.getItem( sceneDef.getTutorialImageList()[_tutorial_imageIndex].name);
            var context = _tutorial_canvas.getContext("2d");
            context.drawImage(tutorialImage, 0, 0, _tutorial_canvas.width, _tutorial_canvas.height);
            _tutorial_imageIndex++;
            setTimeout(function() {graphicEngine.renderTutorial();}, 5000);
        } else {
            _tutorial_canvas.parentNode.removeChild(_tutorial_canvas);
            gameEngine.run();

        }
    }

    this.init = function(FPS, UI_canvas, background_canvas, game_canvas, progress_bar_canvas) {
        _FPS = FPS;

        _UI_canvas = UI_canvas;
        _background_canvas = background_canvas;
        _game_canvas = game_canvas;
        _progress_bar_canvas = progress_bar_canvas;
        _debug_canvas = document.getElementById("DEBUG_canvas");
        _tutorial_canvas = document.getElementById("tutorial_canvas");

        _imageLib = new HashTable();
        _imagesLoadedNum = 0;
        _imagesToLoadNum = 0;
    };

    this.setScene = function(scene) {
        _scene = scene;
    };

    this.getBackgroundImage = function() {
        return  _imageLib.getItem(_scene.backgroundList[_scene.Stages[_scene.currentStage].index].name);
    }

    this.load = function() {

        var loading = document.createElement('p');
        loading.innerHTML = "loading sprites";
        document.getElementById("debug").appendChild(loading);


        // load background images
        var backgroundList = sceneDef.getBackgroundList();
        for (var i=0; i<backgroundList.length; i++){
            _imagesToLoadNum++;
            var imageName = document.createElement("img");
            imageName.id = backgroundList[i].name;
            imageName.src = backgroundList[i].imageSrc;
            imageName.onload = imageOnLoad;
        }

        var tutorialLIst = sceneDef.getTutorialImageList();
        for (var i=0; i<tutorialLIst.length; i++){
            _imagesToLoadNum++;
            var imageName = document.createElement("img");
            imageName.id = tutorialLIst[i].name;
            imageName.src = tutorialLIst[i].imageSrc;
            imageName.onload = imageOnLoad;
        }

        // load obstacle images
        var obstacleList = sceneDef.getObstacleList();
        for(var i=0; i<obstacleList.length; i++){
            _imagesToLoadNum++;
            var obsName = document.createElement("img");
            obsName.id = obstacleList[i].name;
            obsName.src = obstacleList[i].imageSrc;
            obsName.onload = imageOnLoad;
        }

        // load sprites
        var spritesList = sceneDef.getCharacterSpriteList();
        for (var character in spritesList) {
            for (var obj in spritesList[character]) {
                _imagesToLoadNum += spritesList[character][obj].length;
                for (var i=0; i<spritesList[character][obj].length; i++) {
                    var newSprite = document.createElement("img");
                    var spriteName = character.split("Sprites")[0];
                    newSprite.id = spriteName + '_' + obj + i ;
                    newSprite.src = spritesList[character][obj].src + '/' +  i + '.png';
                    newSprite.onload = imageOnLoad;
                }
            }
        }

    };

    this.render = function() {
        // render
        renderUI();

        renderBackground();

        renderCharacters();

        renderDebug();

        // if game end
        if (_gameEnd) {
            var context = _game_canvas.getContext("2d");
            context.font = "110px Arial Black";
            context.fillStyle = '#000000';
            context.fillText("YOU LOSER!", 20, 220);
        }
        if (_gameWin) {
            var context = _game_canvas.getContext("2d");
            context.font = "110px Arial Black";
            context.fillStyle = '#000000';
            context.fillText("YOU WIN!", 120, 220);
        }

    };

    // @author U095159L VARUN GANESH
    function setObstacles() {
        for (var x = 0; x < TILE_MAP.COLS; x = x + 5)
        { 
            for (var y = 0; y < TILE_MAP.ROWS; y = y + 5)
            {
                //Give each a 25% chance of being an obstacle
                var rand = Math.floor(Math.random()*3)
                //0 = open, 1 = obstacle
                if (rand == 0){
                    if((x > _obstacleMargin && y > _obstacleMargin )&&(x < (TILE_MAP.COLS - _obstacleMargin) && y < TILE_MAP.ROWS - _obstacleMargin)){
                        var type = Math.floor(Math.random() * 4);
                        if(type == 0)
                        {//TODO: Randomise between different obstacles
                            //name, index, render_x, render_y, size, trans_x, trans_y
                             _obstacleGrid[y][x] = new Obstacle("table", 2, 2, 1, 1, 2, 1 );
                        }
                        else if(type == 1){
                            _obstacleGrid[y][x] = new Obstacle("vending", 5, 1, 2, 2, 3, 3 );
                        }
                        else if(type == 2){
                             _obstacleGrid[y][x] = new Obstacle("table", 2, 2, 1, 1, 2, 1 );
                        }
                        else{
                             _obstacleGrid[y][x] = new Obstacle("dustbin", 3, 1, 1, 2, 2, 2);
                        }
                        updateObstacleGrid(x, y, _obstacleGrid[y][x].x_disp, _obstacleGrid[y][x].y_disp);
                    }
                }
                else{
                    _obstacleGrid[y][x] = new Obstacle("nothing", -1, 0, 0);
                    TILE_MAP.GRIDS[y][x] = 0;
                }
            }
        }
    }

    // @author U095159L VARUN GANESH
    //Sets the grid to denote obstacles according to size of obstacle
    function updateObstacleGrid( x, y, x_disp, y_disp ) {
        for (var i = x - x_disp; i <= x + x_disp; i++)
        { 
            for (var j = y - y_disp; j <= y + y_disp; j++)
            {
                TILE_MAP.GRIDS[j][i] = 1;
            }
        }
    }

    function renderUI() {
        var context = _UI_canvas.getContext("2d");
        context.fillStyle = '#FFFFFF'; // set canvas background color
        context.fillRect(0, 0, _UI_canvas.width, _UI_canvas.height);

        context.lineWidth = 7;
        context.strokeStyle = 'black';
        context.stroke();
        context.strokeRect(0, 0, _UI_canvas.width, _UI_canvas.height);

        // render food bar
        context.font = "40px Arial Black";
        context.fillStyle = '#000000';
        context.fillText("FOOD", 10, 40);
        context.strokeStyle = "black";
        context.lineWidth = 5;
        context.strokeRect(10, 50, 180, 35);
        context.fillStyle = "#B5E51D";
        var foodBarLength = 175 / _scene.Player.foodMax * _scene.Player.foodCount;
        context.fillRect(13, 53, foodBarLength, 29);

        context.fillStyle = '#000000';
        // render time left
        context.font = "70px Arial Black";
        var timeLeft = _scene.Stages[_scene.currentStage].timeLeft;
        var sec = (timeLeft-timeLeft%1000)/1000;
        var min = (sec - sec%60) / 60;
        var sec = sec%60;
        if (sec < 10)
            sec = '0' + sec.toString();
        context.fillText(min.toString() + ":" + sec, 300, 75);

        // render combo bar
        context.font = "40px Arial Black";
        context.fillText("COMBO", 600, 40);
        context.strokeStyle = "black";
        context.lineWidth = 5;
        context.strokeRect(600, 50, 180, 35);
        context.fillStyle = "#FEF200";
        var comboBarLength = 175 / _scene.Player.comboMax * _scene.Player.comboCount;
        context.fillRect(603, 53, comboBarLength, 29);

        renderProgressbar();

    };

    function renderProgressbar() {

        // render progress bar
        context = _progress_bar_canvas.getContext("2d");
        context.fillStyle = '#FFFFFF'; // set canvas background color
        context.fillRect(0, 0, _progress_bar_canvas.width, _progress_bar_canvas.height);

        context.lineWidth = 4;
        context.beginPath();
        context.moveTo(5, 5);
        context.lineTo(5, 45);
        context.moveTo(5, 30);
        context.lineTo(795, 30);
        context.moveTo(795, 5);
        context.lineTo(795, 45);
        context.stroke();

        // draw progress
        var plannedProgressFigure = _imageLib.getItem('Student_stillRight0');
        var currentProgressFigure = _imageLib.getItem('Player_stillRight0');
        var backgroundImage = _imageLib.getItem(_scene.backgroundList[_scene.Stages[_scene.currentStage].index].name);
        var start = 5;
        var length = 790;
        var ratio = _scene.Player.x / backgroundImage.width;
        var end = start + length * ratio;
        context.drawImage(currentProgressFigure, end, 5, 35, 35);


        var timeElapsed = _scene.Stages[_scene.currentStage].timeMax - _scene.Stages[_scene.currentStage].timeLeft;
        ratio = timeElapsed / _scene.Stages[_scene.currentStage].timeMax;
        end = start + length * ratio;
        context.drawImage(plannedProgressFigure, end, 5, 35, 35);


    }

    function renderBackground() {
        var context = _background_canvas.getContext("2d");
        context.fillStyle = '#FFFFFF'; // set canvas background color
        context.fillRect(0, 0, _UI_canvas.width, _UI_canvas.height);
        var backgroundImage = _imageLib.getItem(_scene.backgroundList[_scene.Stages[_scene.currentStage].index].name);

        var sx = camera.posX - _background_canvas.width/2;
        var sy = 50; // hard coded. need to adjust background image
        var swidth = _background_canvas.width;
        var sheight = _background_canvas.height;
        context.drawImage(backgroundImage, sx, sy, swidth, sheight, 0, 0, _background_canvas.width, _background_canvas.height)
    };

    // @author U095159L VARUN GANESH
    function renderObstacles(){
        var context = _game_canvas.getContext("2d");
        var start_tile = Math.floor((camera.posX - camera.viewWidth/2)/TILE_RESOLUTION);
        var end_tile = Math.floor((camera.posX + camera.viewWidth/2 + (3*TILE_RESOLUTION))/TILE_RESOLUTION);
        for (var x = start_tile; x < end_tile; x++)
        {
            for (var y = 0; y < TILE_MAP.ROWS; y++)
            {
                if (_obstacleGrid[y][x].index > -1) {
                    var obstacleImage = _imageLib.getItem(_scene.obstacleList[_obstacleGrid[y][x].index].name);
                    var gridObstacle = _obstacleGrid[y][x];
                    if(!((x == _scene.Player.tileX) && (y == _scene.Player.tileY))){
                        context.drawImage(obstacleImage, x*TILE_RESOLUTION - obstacleImage.width/2 + (gridObstacle.trans_x) * TILE_RESOLUTION,
                            y*TILE_RESOLUTION - obstacleImage.height/2 + (gridObstacle.trans_y) * TILE_RESOLUTION,
                            obstacleImage.width/gridObstacle.resize, obstacleImage.height/gridObstacle.resize);
                    }
                }
            }
        }
    };
    
    function renderCharacters() {
        // render player
        var context = _game_canvas.getContext("2d");
        context.fillStyle = '#FFFFFF'; // set canvas background color
        context.fillRect(0, 0, _game_canvas.width, _game_canvas.height);


        context.save();
        // set to the correct camera position
        context.translate(-camera.posX+camera.viewWidth/2,0);
        renderObstacles();

        for (var i=0; i<_scene.Student.length; i++) {
            if (_scene.Student[i].alive)
                _scene.Student[i].draw(context, _imageLib);
        }
        for (var i=0; i<_scene.Professor.length; i++) {
            if (_scene.Professor[i].alive) {
                _scene.Professor[i].draw(context, _imageLib);
            }
        }
        for (var i=0; i<_scene.Guard.length; i++) {
            if (_scene.Guard[i].alive) {
                _scene.Guard[i].draw(context, _imageLib);
            }
        }

        _scene.Player.draw(context, _imageLib);



        context.restore();
    };

    function renderDebug() {


        if(DEBUG_MODE_INFO == true){
            var context = _debug_canvas.getContext("2d");
            context.save();


            var type = ai.getCurrentWaveType();
            context.fillStyle = '#FFFFFF'; // set canvas background color
            context.fillRect(0, 0, _debug_canvas.width, _debug_canvas.height);

            // render current performance
            context.fillStyle = "black";
            context.font = "20px Arial";
            context.fillText("Current Wave Type: ", 0, 40);
            context.fillText(type.toString(), 0, 70);
            context.fillText("Current Performance: ", 0, 100);
            context.fillText(ai.determinePlayerPerformance().type.toString(), 0, 130);

            // render food tension curve
            context.fillText("Food Tension Curve: ", 0, 180);
            context.strokeStyle = "black";
            context.lineWidth = 4;
            context.beginPath();
            context.moveTo(0, 190);
            context.lineTo(0, 293);
            context.moveTo(0, 293);
            context.lineTo(200, 293);
            context.stroke();
            context.beginPath();
            context.strokeStyle = "red";
            for (var j= 0, i=ai.currentHistoryIndex; j<ai.foodTensionHistory.length-1; j++, i=(i+1) % ai.foodTensionHistory.length) {
                var startPointX = 0 + j*200/ai.foodTensionHistory.length;
                var startPointY = 290 - ai.foodTensionHistory[i];
                var endPointX = 0 + (j+1)*200/ai.foodTensionHistory.length;
                var endPointY = 290 - ai.foodTensionHistory[i+1];
                context.moveTo(startPointX, startPointY);
                context.lineTo(endPointX, endPointY);
            }
            context.stroke();

            // render TIME tension curve
            context.fillText("Time Tension Curve: ", 0, 315);
            context.strokeStyle = "black";
            context.lineWidth = 4;
            context.beginPath();
            context.moveTo(0, 320);
            context.lineTo(0, 423);
            context.moveTo(0, 423);
            context.lineTo(200, 423);
            context.stroke();
            context.beginPath();
            context.strokeStyle = "red";
            for (var j= 0, i=ai.currentHistoryIndex; j<ai.timeTensionHistory.length-1; j++, i=(i+1) % ai.timeTensionHistory.length) {
                var startPointX = 0 + j*200/ai.timeTensionHistory.length;
                var startPointY = 420 - ai.timeTensionHistory[i] * 200;
                if (startPointY < 320)
                    startPointY = 320;
                var endPointX = 0 + (j+1)*200/ai.timeTensionHistory.length;
                var endPointY = 420 - ai.timeTensionHistory[i+1] * 200;
                if (endPointY < 320)
                    endPointY = 320;
                context.moveTo(startPointX, startPointY);
                context.lineTo(endPointX, endPointY);
            }
            context.stroke();

            context.restore();

        } else {
            var context = _debug_canvas.getContext("2d");
            context.save();
            // render current performance
            var type = ai.getCurrentWaveType();
            context.fillStyle = '#FFFFFF'; // set canvas background color
            context.fillRect(0, 0, _debug_canvas.width, _debug_canvas.height);
        }

        if (DEBUG_MODE === true) {
            // render debug on game canvas
            context = _game_canvas.getContext("2d");
            context.save();
            // render the tile grids
            context.translate(-camera.posX+camera.viewWidth/2,0);
            for(var x = 0; x < TILE_MAP.COLS; x++){
                for(var y = 0; y < TILE_MAP.ROWS; y++){
                    context.strokeStyle = "black";
                    context.strokeRect(x*TILE_RESOLUTION, y*TILE_RESOLUTION, TILE_RESOLUTION, TILE_RESOLUTION);
                    context.fillStyle = "black";
                    if (TILE_MAP.GRIDS[y][x] === 1)
                        context.fillRect(x*TILE_RESOLUTION, y*TILE_RESOLUTION, TILE_RESOLUTION, TILE_RESOLUTION);
                }
            }

            // render the wave trigger line
            var backgroundImage = _imageLib.getItem(_scene.backgroundList[_scene.Stages[_scene.currentStage].index].name);
            for (var i=0; i<10; i++) {
                var x = backgroundImage.width * (i+1) / 10;
                context.beginPath();
                context.strokeStyle = "cyan";
                if (ai.getWaveIndex() === i)
                    context.strokeStyle = "red";
                context.lineWidth = 4;
                context.moveTo(x, 0);
                context.lineTo(x, _game_canvas.height);
                context.stroke();
            }


            // render the character's bound box
            context.restore();
        }

     };
}
var graphicEngine = new GraphicEngine();